<!-- 监测点树 -->

<template>
  <EasyTreeMulti v-model="model" v-bind="treeProps" @idChange="emitChange" @expand="toExpand" @collapse="toCollapse">
    <template #top>
      <el-checkbox v-model="isAll" :indeterminate="isHalf" :key="`${isAll}${isHalf}`" @change="changeAll">
        全选
      </el-checkbox>

      <el-checkbox v-model="isLink">级联选择</el-checkbox>
    </template>

    <template #default="{ data, nodeClass }">
      <slot name="node" :data="data" :nodeClass="nodeClass" />
    </template>
  </EasyTreeMulti>
</template>

<script>
// ============================== 导入依赖 ============================== //

import CORE from '@/CORE'
import props from './props'

const getTreeAllIds = (allIds, tree, field) => {
  tree.forEach((item) => {
    const [children, id] = field.slice(0, 2).map((key, i) => item[key || ''] || (i ? '' : []))
    allIds.push(id)
    getTreeAllIds(allIds, children, field)
  })
  return allIds
}

// ============================== 导出组件 ============================== //

export default {
  /**
   * 名称定义 (实际使用名称)
   */
  name: 'PointTree',

  /**
   * 混入列表 (配置选项合并)
   */
  mixins: [
    CORE.mixWeb(),
    CORE.mixTree('elec', 'elec_'),
    CORE.mixTree('water', 'water_'),
    CORE.mixTree('gas', 'gas_'),
  ],

  /**
   * 属性注册 (抽取以便查阅)
   */
  props,

  /**
   * 状态数据 (函数返回对象)
   */
  data() {
    return {
      model: [], // 内部绑定值
      EMPTY: [], // 空值常量

      isAll: false, // 是否全选
      isHalf: false, // 是否半选

      isLink: false, // 是否级联
    }
  },

  /**
   * 计算属性 (慎用箭头函数)
   */
  computed: {
    /**
     * 前缀
     */
    PRE() {
      const { param: { energyType } } = this
      return `${CORE.dict(CORE.DICT.ENERGY_TYPE, energyType, 2)}_`
    },

    /**
     * 结构树属性
     */
    treeProps() {
      const { PRE } = this

      return {
        tree: this[`${PRE}tree`],
        field: this[`${PRE}TREE_FIELD`],
        filterTip: '监测点名称',
        noLinkage: !this.isLink,
      }
    },

    /**
     * 所有 ID 列表
     */
    allIdList() {
      const { tree, field } = this.treeProps
      return getTreeAllIds([], tree, field)
    },
  },

  /**
   * 监听属性 (深度 deep 首次 immediate)
   */
  watch: {
    /**
     * 监听所属对象
     */
    param: {
      deep: true,
      immediate: true,
      handler() {
        const { isDiff, setModel, emitChange } = this
        if (isDiff()) {
          setModel()
          emitChange()
        }
      },
    },
  },

  /**
   * 创建完成 (先抽取再调用)
   */
  created() {
    const { param, bind, $set } = this
    if (!Object.keys(param || {}).includes(bind)) $set(param, bind, '')
  },

  /**
   * 本地方法 (轻量重要在前)
   */
  methods: {
    /**
     * 是否不等 (可覆盖)
     */
    isDiff() {
      const { param, bind, model } = this
      if (!CORE.isArray(param[bind])) return true
      return param[bind].join() !== model.join()
    },

    /**
     * 设置内部绑定值 (可覆盖)
     */
    setModel() {
      const { param, bind, EMPTY } = this
      this.model = CORE.isArray(param[bind]) ? param[bind] : EMPTY
    },

    /**
     * 获取最终回传值 (可覆盖)
     */
    getValues() {
      const { model } = this
      return model
    },

    /**
     * 是否相等 (可覆盖)
     */
    equals(values) {
      const { param, bind } = this
      if (!CORE.isArray(param[bind])) return false
      return param[bind].join() === values.join()
    },

    /**
     * 提交改变 (可覆盖)
     */
    emitChange() {
      const { setAllHalf, getValues, equals, param, bind, PRE } = this

      // 设置全选半选
      setAllHalf()

      // 获取新值
      const values = getValues()

      // 判断改变
      if (equals(values)) return

      // 更新绑定
      this.$set(param, bind, values)
      this.$emit('change', values.map((id) => this[`${PRE}treeMap`][id] || {}))

      // 刷新列表
      this.startList(1)
    },

    /**
     * 点击展开
     */
    toExpand(data) {
      this.$emit('expand', data)
    },

    /**
     * 点击收缩
     */
    toCollapse(data) {
      this.$emit('collapse', data)
    },

    /**
     * 切换全选
     */
    changeAll(isAll) {
      this.model = this.isHalf || isAll ? this.allIdList : []
      this.isHalf = false
      this.emitChange()
    },

    /**
     * 设置全选半选
     */
    setAllHalf() {
      this.isAll = this.model.length > 0
      this.isHalf = this.isAll && this.model.length < this.allIdList.length
    },
  },
}
</script>
